// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build aix | | darwin | |蜻蜓| | freebsd | | linux | | netbsd | openbsd | | solaris 
// /+build aix darwin drawnfly freebsd linux netbsd openbsd solaris 

package runtime

import (
	"runtime/internal/atomic"
	"unsafe"
)

// sigtab是全局sigtable数组中的条目类型。
// sigtable本质上依赖于系统，并出现在操作系统特定的文件中，
// 但sigTabT对于所有Unixy系统都是相同的。
// sigtable数组通过系统信号编号进行索引，以获取标志
// 和每个信号的可打印名称。
type sigTabT struct {
	flags int32
	name  string
}

// go:linkname os_sigpipe os.sigpipe 
func os_sigpipe() {
	systemstack(sigpipe)
}

func signame(sig uint32) string {
	if sig >= uint32(len(sigtable)) {
		return ""
	}
	return sigtable[sig].name
}

const (
	_SIG_DFL uintptr = 0
	_SIG_IGN uintptr = 1
)

// sigpremept是用于非合作抢占的信号。
// 
// 没有选择此信号的好方法，但有一些
// 启发式方法：
// 
// 1。它应该是调试程序通过
// 默认值传递的信号。在Linux上，这是SIGALRM、SIGURG、SIGCHLD、SIGIO、
// SIGVTALRM、SIGPROF和SIGWINCH，以及一些glibc内部信号。中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网。libc不应该在混合Go/C二进制文件
// 中内部使用它，因为libc可能认为它是唯一可以处理这些
// 信号的东西。例如SIGCANCEL或SIGSETXID。中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网中国英语学习网。这应该是一个信号，可以发生虚假的
// 后果。例如，SIGALRM是一个错误的选择，因为
// 信号处理程序无法判断它是否是由实际进程
// 警报引起的（可以说这意味着信号被破坏了，但我
// 离题了）。SIGUSR1和SIGUSR2也不好，因为它们经常被应用程序以有意义的方式使用。中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译：中国英语学习网编译。我们需要处理没有实时信号的平台（比如
// macOS），所以这些平台已经过时了。
// 
// 我们之所以使用SIGURG，是因为它符合所有这些标准，因为它的“真实”含义极不可能被应用程序使用（因为带外数据基本上没有使用，而且SIGURG-
// 没有报告哪个套接字有这种情况，这使得它非常
// 无用），即使是这样，应用程序也必须准备好使用
// 伪SIGURG。SIGIO也不是一个坏选择，但更可能用于实际。
const sigPreempt = _SIGURG

// 存储在Go安装自己之前注册的信号处理程序。
// 在以下情况下将调用这些信号处理程序：Go不想处理特定的信号（例如，信号发生在非Go线程上）.
// 有关何时转发信号的详细信息，请参见sigfwdgo。
// 
// 这由信号处理程序读取；访问应使用
// atomic.LoadingTPTR和atomic.StoreUINTPTTR。
var fwdSig [_NSIG]uintptr

// handlingSig按信号号索引，如果我们是
// 这是uint32而不是bool，以便我们可以使用原子指令。
var handlingSig [_NSIG]uint32

// 用于将信号掩码更新与信号掩码
var (
	disableSigChan  chan uint32
	enableSigChan   chan uint32
	maskUpdatedChan chan struct{}
)

func init() {
	// \NSIG是此操作系统上的信号数。
	// sigtable应说明如何处理所有可能的信号。
	if len(sigtable) != _NSIG {
		print("runtime: len(sigtable)=", len(sigtable), " _NSIG=", _NSIG, "\n")
		throw("bad sigtable len")
	}
}

var signalsOK bool

// 初始化信号。
// 由libpreinit调用，因此运行时可能无法初始化。
// go:nosplit 
// go:nowritebarrierrec 
func initsig(preinit bool) {
	if !preinit {
		// 现在可以运行信号处理程序了。
		signalsOK = true
	}

	// 对于c-archive/c-shared，这是libpreinit用
	// preinit==true调用的。
	if (isarchive || islibrary) && !preinit {
		return
	}

	for i := uint32(0); i < _NSIG; i++ {
		t := &sigtable[i]
		if t.flags == 0 || t.flags&_SigDefault != 0 {
			continue
		}

		// 我们不需要在这里使用原子操作，因为
		// 还不应该有任何其他goroutin正在运行。
		fwdSig[i] = getsig(i)

		if !sigInstallGoHandler(i) {
			// 即使我们没有安装信号处理程序，
			// 如果需要，设置SAU ONSTACK。
			if fwdSig[i] != _SIG_DFL && fwdSig[i] != _SIG_IGN {
				setsigstack(i)
			} else if fwdSig[i] == _SIG_IGN {
				sigInitIgnored(i)
			}
			continue
		}

		handlingSig[i] = 1
		setsig(i, funcPC(sighandler))
	}
}

// go:nosplit 
// go:nowritebarrierrec 
func sigInstallGoHandler(sig uint32) bool {
	// 对于某些信号，我们尊重继承的信号处理程序
	// 而不是坚持安装我们自己的默认处理程序。
	// 即使这些信号也可以使用os/signal软件包获取。
	switch sig {
	case _SIGHUP, _SIGINT:
		if atomic.Loaduintptr(&fwdSig[sig]) == _SIG_IGN {
			return false
		}
	}

	t := &sigtable[sig]
	if t.flags&_SigSetStack != 0 {
		return false
	}

	// 使用c-archive或c-shared构建时，仅安装信号
	// 同步信号和SIGPIPE.
	if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE {
		return false
	}

	return true
}

// SIGNABLE使Go信号处理程序能够捕获信号sig。
// 仅在持有os/signal.handlers锁时调用，
// 通过os/signal.enableSignal和signal_enable调用。
func sigenable(sig uint32) {
	if sig >= uint32(len(sigtable)) {
		return
	}

	// SIGPROF专门用于评测。
	if sig == _SIGPROF {
		return
	}

	t := &sigtable[sig]
	if t.flags&_SigNotify != 0 {
		ensureSigM()
		enableSigChan <- sig
		<-maskUpdatedChan
		if atomic.Cas(&handlingSig[sig], 0, 1) {
			atomic.Storeuintptr(&fwdSig[sig], getsig(sig))
			setsig(sig, funcPC(sighandler))
		}
	}
}

// SIGDABLE禁用信号sig的Go信号处理程序。
// 仅在持有os/signal.handlers锁时调用，
// 通过os/signal.disableSignal和signal_disable调用。
func sigdisable(sig uint32) {
	if sig >= uint32(len(sigtable)) {
		return
	}

	// SIGPROF是专门为评测而处理的。
	if sig == _SIGPROF {
		return
	}

	t := &sigtable[sig]
	if t.flags&_SigNotify != 0 {
		ensureSigM()
		disableSigChan <- sig
		<-maskUpdatedChan

		// 如果initsig没有为
		// 信号安装信号处理程序，那么要返回到通知
		// 之前的状态，我们应该删除已安装的信号处理程序。
		if !sigInstallGoHandler(sig) {
			atomic.Store(&handlingSig[sig], 0)
			setsig(sig, atomic.Loaduintptr(&fwdSig[sig]))
		}
	}
}

// SIGINORE忽略信号sig。
// 仅在持有os/signal.handlers锁时调用，
// 通过os/signal.ignoreSignal和signal_ignore调用。
func sigignore(sig uint32) {
	if sig >= uint32(len(sigtable)) {
		return
	}

	// SIGPROF是专门为评测而处理的。
	if sig == _SIGPROF {
		return
	}

	t := &sigtable[sig]
	if t.flags&_SigNotify != 0 {
		atomic.Store(&handlingSig[sig], 0)
		setsig(sig, _SIG_IGN)
	}
}

// 清除信号处理程序清除所有未被忽略的信号处理程序
// 返回默认值。这是由子级在fork之后调用的，这样我们就可以为exec启用信号掩码，而不用担心在子级中运行信号处理程序。
// go:nosplit 
// go:nowritebarrierrec 
func clearSignalHandlers() {
	for i := uint32(0); i < _NSIG; i++ {
		if atomic.Load(&handlingSig[i]) != 0 {
			setsig(i, _SIG_DFL)
		}
	}
}

// /setProcessCPupFiler在分析计时器更改时被调用。
// 在按住prof.lock的情况下调用它。hz是新计时器，如果
// 分析被禁用，则hz为0。启用或禁用信号
// 对于-buildmode=c-archive是必需的。
func setProcessCPUProfiler(hz int32) {
	if hz != 0 {
		// 如果未启用，则启用Go信号处理程序。
		if atomic.Cas(&handlingSig[_SIGPROF], 0, 1) {
			atomic.Storeuintptr(&fwdSig[_SIGPROF], getsig(_SIGPROF))
			setsig(_SIGPROF, funcPC(sighandler))
		}

		var it itimerval
		it.it_interval.tv_sec = 0
		it.it_interval.set_usec(1000000 / hz)
		it.it_value = it.it_interval
		setitimer(_ITIMER_PROF, &it, nil)
	} else {
		setitimer(_ITIMER_PROF, &itimerval{}, nil)

		// 如果默认情况下应该禁用Go信号处理程序，则
		// 切换回启用评测时安装的信号处理程序
		// 。我们不尝试处理程序在启用Go 
		// 评测时更改SIGPROF处理程序的情况。
		// 
		// 如果之前未安装信号处理程序，则启动
		// 忽略SIGPROF信号。我们这样做，而不是将
		// 更改为SIG_DFL，因为可能存在尚未传递到其他线程的挂起的SIGPROF 
		// 信号。
		// 如果我们在此处更改为SIG_DFL，则当该SIGPROF交付时，程序将崩溃。我们假设使用评测的程序
		// 不希望在一个错误的SIGPROF上崩溃。
		// 见19320期。
		if !sigInstallGoHandler(_SIGPROF) {
			if atomic.Cas(&handlingSig[_SIGPROF], 1, 0) {
				h := atomic.Loaduintptr(&fwdSig[_SIGPROF])
				if h == _SIG_DFL {
					h = _SIG_IGN
				}
				setsig(_SIGPROF, h)
			}
		}
	}
}

// SetThreadCPupFiler对
// 以hz的速率执行评测所需的任何特定于线程的更改。
// 在Unix系统上无需更改。
func setThreadCPUProfiler(hz int32) {
	getg().m.profilehz = hz
}

func sigpipe() {
	if signal_ignored(_SIGPIPE) || sigsend(_SIGPIPE) {
		return
	}
	dieFromSignal(_SIGPIPE)
}

// doSigPreempt处理gp上的抢占信号。
func doSigPreempt(gp *g, ctxt *sigctxt) {
	// 检查此G是否要被抢占并且对
	// 抢占安全。
	if wantAsyncPreempt(gp) {
		if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()); ok {
			// 调整PC并注入对asyncPreempt的调用。
			ctxt.pushCall(funcPC(asyncPreempt), newpc)
		}
	}

	// 确认优先购买权。
	atomic.Xadd(&gp.m.preemptGen, 1)
	atomic.Store(&gp.m.signalPending, 0)

	if GOOS == "darwin" || GOOS == "ios" {
		atomic.Xadd(&pendingPreemptSignals, -1)
	}
}

const preemptMSupported = true

// preemptM向mp发送抢占请求。此请求可以是异步处理的
// 并且可以与其他对
// M的请求合并。当收到请求时，如果正在运行的G或P被
// 标记为抢占，并且goroutine处于异步
// 安全点，它将抢占goroutine。它总是在处理抢占请求后自动增加mp.preemptGen。
func preemptM(mp *m) {
	// 关于达尔文，不要试图在执行期间抢占线程。
	// 问题#41702。
	if GOOS == "darwin" || GOOS == "ios" {
		execLock.rlock()
	}

	if atomic.Cas(&mp.signalPending, 0, 1) {
		if GOOS == "darwin" || GOOS == "ios" {
			atomic.Xadd(&pendingPreemptSignals, 1)
		}

		// 如果多个线程抢占同一个M，它可能会向同一个M发送多个
		// 信号，使其几乎无法取得进展，从而导致
		// 活锁问题。显然，这可能发生在达尔文身上。见
		// 第37741期。
		// 仅在没有挂起信号时发送信号。
		signalM(mp, sigPreempt)
	}

	if GOOS == "darwin" || GOOS == "ios" {
		execLock.runlock()
	}
}

// sigFetchG在信号处理程序中运行时安全地获取G的值。
// 在某些体系结构上，在VDSO中运行时，g值可能会被删除。
// 见第32912期。
// 
// go:nosplit 
func sigFetchG(c *sigctxt) *g {
	switch GOARCH {
	case "arm", "arm64", "ppc64", "ppc64le":
		if !iscgo && inVDSOPage(c.sigpc()) {
			// 使用cgo时，我们将g保存在TLS上，并从那里加载到sigtramp中。就用这个吧。
			// 否则，在进行VDSO调用之前，我们将g保存到信号堆栈的底部。从那里取回。
			// TODO:在efence模式下，堆栈是系统分配的，所以这不起作用。
			sp := getcallersp()
			s := spanOf(sp)
			if s != nil && s.state.get() == mSpanManual && s.base() < sp && sp < s.limit {
				gp := *(**g)(unsafe.Pointer(s.base()))
				return gp
			}
			return nil
		}
	}
	return getg()
}

// sigtrampgo是从信号处理函数sigtramp、
// 用汇编代码编写。
// 这是由信号处理程序调用的，世界可能会停止。
// 
// 它必须是nosplit，因为getg（）仍然是在发送信号时运行
// （如果有）的G，但它（通常）在gsignal堆栈上称为
// 。在将G切换为gsignal之前，
// 堆栈边界检查将无法工作。
// 
// go:nosplit 
// go:nowritebarrierrec 
func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
	if sigfwdgo(sig, info, ctx) {
		return
	}
	c := &sigctxt{info, ctx}
	g := sigFetchG(c)
	setg(g)
	if g == nil {
		if sig == _SIGPROF {
			sigprofNonGoPC(c.sigpc())
			return
		}
		if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 {
			// 这可能是来自preemptM的信号，在执行go代码时发送
			// 但在执行非go代码时接收。
			// 我们通过了sigfwdgo，所以我们知道有
			// 没有SIGFREEMPT的非Go信号处理程序。
			// sigpremept的默认行为是忽略信号，因此badsignal无论如何都是无效的。
			if GOOS == "darwin" || GOOS == "ios" {
				atomic.Xadd(&pendingPreemptSignals, -1)
			}
			return
		}
		c.fixsigcode(sig)
		badsignal(uintptr(sig), c)
		return
	}

	setg(g.m.gsignal)

	// 如果某个非Go代码名为sigaltstack，请调整。
	var gsignalStack gsignalStack
	setStack := adjustSignalStack(sig, g.m, &gsignalStack)
	if setStack {
		g.m.gsignal.stktopsp = getcallersp()
	}

	if g.stackguard0 == stackFork {
		signalDuringFork(sig)
	}

	c.fixsigcode(sig)
	sighandler(sig, info, ctx, g)
	setg(g)
	if setStack {
		restoreGsignalStack(&gsignalStack)
	}
}

// adjustSignalStack根据处理信号时实际使用的堆栈指针调整当前堆栈保护。
// 我们这样做是为了防止一些名为sigaltstack的非Go代码。
// 报告堆栈是否已调整，如果已调整，则将旧的
// 信号堆栈存储在*gsigstack中。
// go:nosplit 
func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool {
	sp := uintptr(unsafe.Pointer(&sig))
	if sp >= mp.gsignal.stack.lo && sp < mp.gsignal.stack.hi {
		return false
	}

	var st stackt
	sigaltstack(nil, &st)
	stsp := uintptr(unsafe.Pointer(st.ss_sp))
	if st.ss_flags&_SS_DISABLE == 0 && sp >= stsp && sp < stsp+st.ss_size {
		setGsignalStack(&st, gsigStack)
		return true
	}

	if sp >= mp.g0.stack.lo && sp < mp.g0.stack.hi {
		// 信号在g0堆栈上传递。
		// 当使用线程清理器与C代码
		// 链接时，可能会发生这种情况，线程清理器收集
		// 信号，然后通过调用
		// 直接发送信号处理程序，当C代码
		// 包括通过cgo调用的C代码时，调用
		// TSAN拦截函数，如malloc。
		// 
		// 我们最后检查此条件，因为g0.stack.lo 
		// 可能不太准确（请参见MSSTART）。
		st := stackt{ss_size: mp.g0.stack.hi - mp.g0.stack.lo}
		setSignalstackSP(&st, mp.g0.stack.lo)
		setGsignalStack(&st, gsigStack)
		return true
	}

	// sp不在gsignal堆栈、g0堆栈或sigaltstack中。令人不快的
	setg(nil)
	needm()
	if st.ss_flags&_SS_DISABLE != 0 {
		noSignalStack(sig)
	} else {
		sigNotOnStack(sig)
	}
	dropm()
	return false
}

// crashing是我们在实现
// GOTRACEBACK=crash时在接收到信号时等待的m数。
var crashing int32

// testSigtrap和testSigusr1用于运行时测试。如果
// 非nil，则在SIGTRAP/SIGUSR1上调用它。如果返回true，
// 此信号的正常行为将被抑制。
var testSigtrap func(info *siginfo, ctxt *sigctxt, gp *g) bool
var testSigusr1 func(gp *g) bool

// 信号发生时调用sighandler。全局g将是
// 设置为gsignal goroutine，我们将在备用
// 信号堆栈上运行。参数g将是信号发生时全局g 
// 的值。sig、info和ctxt参数是系统信号处理程序中的
// 参数：它们是将SA传递给sigaction系统调用时传递的参数。
// 
// 垃圾回收器可能已经停止了世界，因此不允许使用写屏障
// 。
// 
// go:nowritebarrierrec 
func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
	_g_ := getg()
	c := &sigctxt{info, ctxt}

	if sig == _SIGPROF {
		sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, _g_.m)
		return
	}

	if sig == _SIGTRAP && testSigtrap != nil && testSigtrap(info, (*sigctxt)(noescape(unsafe.Pointer(c))), gp) {
		return
	}

	if sig == _SIGUSR1 && testSigusr1 != nil && testSigusr1(gp) {
		return
	}

	if sig == sigPreempt && debug.asyncpreemptoff == 0 {
		// 可能是抢占信号。
		doSigPreempt(gp, c)
		// 即使这确实是一个抢占信号，它也可能与另一个信号结合在一起，所以我们仍然让它通过应用程序。
	}

	flags := int32(_SigThrow)
	if sig < uint32(len(sigtable)) {
		flags = sigtable[sig].flags
	}
	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 && gp.throwsplit {
		// 我们不能安全地使用sigpanic，因为它可能会增加
		// 堆栈。而是在信号处理程序中中止。
		flags = _SigThrow
	}
	if isAbortPC(c.sigpc()) {
		// 在许多体系结构上，abort函数只是
		// 会导致内存故障。不要把这变成恐慌。
		flags = _SigThrow
	}
	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
		// 这个信号会引起恐慌。
		// 排列堆栈，使其看起来像信号发生的点
		// 调用了
		// 函数SIGMANIC。然后将电脑设置为SIGMANIC。

		// 必须将参数传递到带外，因为
		// 增加堆栈帧将破坏
		// 展开代码。
		gp.sig = sig
		gp.sigcode0 = uintptr(c.sigcode())
		gp.sigcode1 = uintptr(c.fault())
		gp.sigpc = c.sigpc()

		c.preparePanic(sig, gp)
		return
	}

	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
		if sigsend(sig) {
			return
		}
	}

	if c.sigcode() == _SI_USER && signal_ignored(sig) {
		return
	}

	if flags&_SigKill != 0 {
		dieFromSignal(sig)
	}

	// /\u sigtrow意味着我们现在应该退出。
	// 如果我们带着_sigmanic来到这里，这意味着信号
	// 是由一个程序发送给我们的（c.sigcode（）==_SI_USER）；
	// 在这种情况下，如果我们没有在sigsend中处理它，我们现在就退出。
	if flags&(_SigThrow|_SigPanic) == 0 {
		return
	}

	_g_.m.throwing = 1
	_g_.m.caughtsig.set(gp)

	if crashing == 0 {
		startpanic_m()
	}

	if sig < uint32(len(sigtable)) {
		print(sigtable[sig].name, "\n")
	} else {
		print("Signal ", sig, "\n")
	}

	print("PC=", hex(c.sigpc()), " m=", _g_.m.id, " sigcode=", c.sigcode(), "\n")
	if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
		print("signal arrived during cgo execution\n")
		gp = _g_.m.lockedg.ptr()
	}
	if sig == _SIGILL || sig == _SIGFPE {
		// 如果能知道指令的长度，那就太好了。
		// 不幸的是，这通常很复杂（主要用于x86 
		// 和s930x，但其他ARCH也有非标准指令长度）。
		// 选择打印16字节，这涵盖了大多数指令。
		const maxN = 16
		n := uintptr(maxN)
		// 但是我们必须小心。如果我们接近
		// 一个页面的末尾，而下一个页面没有映射，我们可能会
		// 故障。因此，请确保我们没有跨页（即使
		// 这可能会导致打印不完整的指令）。
		// 我们在这里假设我们至少可以读取包含PC的页面。
		// 我想页面可能是映射的可执行文件，但不可读？
		pc := c.sigpc()
		if n > physPageSize-pc%physPageSize {
			n = physPageSize - pc%physPageSize
		}
		print("instruction bytes:")
		b := (*[maxN]byte)(unsafe.Pointer(pc))
		for i := uintptr(0); i < n; i++ {
			print(" ", hex(b[i]))
		}
		println()
	}
	print("\n")

	level, _, docrash := gotraceback()
	if level > 0 {
		goroutineheader(gp)
		tracebacktrap(c.sigpc(), c.sigsp(), c.siglr(), gp)
		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
			// 回溯原始m上的其他人跳过了此项；现在追踪它。
			goroutineheader(_g_.m.curg)
			traceback(^uintptr(0), ^uintptr(0), 0, _g_.m.curg)
		} else if crashing == 0 {
			tracebackothers(gp)
			print("\n")
		}
		dumpregs(c)
	}

	if docrash {
		crashing++
		if crashing < mcount()-int32(extraMCount) {
			// 还有其他m需要转储堆栈。
			// 通过将SIGQUIT发送到当前进程，将其中继到下一个m。
			// 所有已经接收到SIGQUIT的m都有信号掩码阻止接收任何信号，因此SIGQUIT将转到尚未看到它的m。
			// 当最后一个m收到SIGQUIT时，它将通过调用下面的
			// 崩溃。为了防止中继出错，
			// /中的每个m都会休眠5秒钟，然后自行崩溃/退出。
			// 在预期的操作中，最后一个m已收到SIGQUIT并运行
			// 崩溃/退出，进程消失，所有这些都是在任何
			// 5秒睡眠结束之前很久的事情。
			print("\n-----\n\n")
			raiseproc(_SIGQUIT)
			usleep(5 * 1000 * 1000)
		}
		crash()
	}

	printDebugLog()

	exit(2)
}

// sigmanic将同步信号转换为运行时死机。
// 如果信号处理程序看到同步死机，它会将
// 堆栈安排为信号发生的函数
// SIGMANIC，将信号的PC值设置为SIGMANIC，并从信号处理程序返回。其效果是，程序将充当
// 尽管获得信号的函数只是简单地称为SIGMANIC 
// 函数。
// 
// 这不能是nosplit，因为链接器不知道哪里可以注入
// SIGRANIC调用。
// 
// 如果
// getg（）.throwsplit，则信号处理程序不得注入对SIGMANIC的调用，因为SIGMANIC可能需要增加堆栈。
// 
// 这是在运行时/cgo中通过linkname导出到程序集的。
// go:linkname sigmanic 
func sigpanic() {
	g := getg()
	if !canpanic(g) {
		throw("unexpected signal during runtime execution")
	}

	switch g.sig {
	case _SIGBUS:
		if g.sigcode0 == _BUS_ADRERR && g.sigcode1 < 0x1000 {
			panicmem()
		}
		// 支持运行时/debug.SetPanicOnFault。
		if g.paniconfault {
			panicmemAddr(g.sigcode1)
		}
		print("unexpected fault address ", hex(g.sigcode1), "\n")
		throw("fault")
	case _SIGSEGV:
		if (g.sigcode0 == 0 || g.sigcode0 == _SEGV_MAPERR || g.sigcode0 == _SEGV_ACCERR) && g.sigcode1 < 0x1000 {
			panicmem()
		}
		// 支持运行时/debug.SetPanicOnFault。
		if g.paniconfault {
			panicmemAddr(g.sigcode1)
		}
		print("unexpected fault address ", hex(g.sigcode1), "\n")
		throw("fault")
	case _SIGFPE:
		switch g.sigcode0 {
		case _FPE_INTDIV:
			panicdivide()
		case _FPE_INTOVF:
			panicoverflow()
		}
		panicfloat()
	}

	if g.sig >= uint32(len(sigtable)) {
		// 不可能：我们在sigtable中查找了g.sig，决定调用sigmanic 
		throw("unexpected signal value")
	}
	panic(errorString(sigtable[g.sig].name))
}

// dieFromSignal用一个信号终止程序。
// 这为shell提供了预期的退出状态。
// 仅使用预期会终止进程的致命信号调用此函数。
// go:nosplit 
// go:nowritebarrierrec 
func dieFromSignal(sig uint32) {
	unblocksig(sig)
	// 将信号标记为未经处理，以确保其已转发。
	atomic.Store(&handlingSig[sig], 0)
	raise(sig)

	// 那应该会杀了我们。但是，在某些系统上，raise 
	// 将信号发送到整个进程，而不仅仅是发送到当前线程，这意味着信号可能尚未发送到
	// 。给其他线程一个运行的机会，并拾取信号。
	osyield()
	osyield()
	osyield()

	// 如果这不起作用，请尝试一下。
	setsig(sig, _SIG_DFL)
	raise(sig)

	osyield()
	osyield()
	osyield()

	// 如果我们仍在运行，请以错误的状态退出。
	exit(2)
}

// raisebadsignal在非Go 
// 线程上接收到信号时被调用，Go程序不想处理它（也就是说，
// 程序没有为信号调用os/signal.Notify）。
func raisebadsignal(sig uint32, c *sigctxt) {
	if sig == _SIGPROF {
		// 忽略到达非Go线程的分析信号。
		return
	}

	var handler uintptr
	if sig >= _NSIG {
		handler = _SIG_DFL
	} else {
		handler = atomic.Loaduintptr(&fwdSig[sig])
	}

	// 重置信号处理器并发出信号。
	// 我们当前正在信号处理程序中运行，因此
	// 信号被阻塞。我们需要在发出
	// 信号之前解除对其的阻止，否则我们发出的信号将被忽略，直到我们从信号处理程序返回
	// 为止。我们知道信号在进入处理程序之前是未被阻止的，否则我们就不会收到它。这意味着我们不必担心再次阻止它。
	unblocksig(sig)
	setsig(sig, handler)

	// 如果我们链接到一个非Go程序中，我们希望尝试
	// 避免修改发出信号
	// 的原始上下文。如果处理程序是默认的，我们知道它是不可恢复的，因此我们不必担心重新安装sighandler。此时，我们可以返回
	// 并且该信号将被重新引发并被
	// 具有正确上下文的默认处理程序捕获。
	// 
	// 在FreeBSD上，libthr sigaction代码阻止
	// 此功能无法正常工作，因此我们无法提高。
	if GOOS != "freebsd" && (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER {
		return
	}

	raise(sig)

	// 给信号一个传递的机会。
	// 在几乎所有实际情况下，程序都将崩溃，所以睡在这里不是浪费时间。
	usleep(1000)

	// 如果信号没有导致程序退出，请恢复
	// Go信号处理程序并继续。
	// 
	// 在恢复Go处理程序之前，我们可能会收到另一个信号实例，但这并不是那么糟糕：我们知道
	// Go程序一直忽略该信号。
	setsig(sig, funcPC(sighandler))
}

// go:nosplit 
func crash() {
	// OS X核心转储是映射内存的线性转储，
	// 从第一个虚拟字节到最后一个虚拟字节，间隙中有零。
	// 由于我们在64位系统上安排地址空间的方式，
	// 这意味着OS X核心文件将大于128 GB，即使在快速的
	// 工作站上也可以花一个多小时来编写OS X（不间断）。
	// 避免用户犯这样的错误。
	if GOOS == "darwin" && GOARCH == "amd64" {
		return
	}

	dieFromSignal(_SIGABRT)
}

// ensureSigM启动一个全局休眠线程，以确保至少有一个线程
// 可用于捕获为操作系统/信号启用的信号。
func ensureSigM() {
	if maskUpdatedChan != nil {
		return
	}
	maskUpdatedChan = make(chan struct{})
	disableSigChan = make(chan uint32)
	enableSigChan = make(chan uint32)
	go func() {
		// 每个线程都有信号掩码，因此请确保此goroutine位于一个
		// 线程上。
		LockOSThread()
		defer UnlockOSThread()
		// sigBlocked掩码包含操作系统/信号的非活动信号，
		// 最初包含除基本信号外的所有信号。调用signal.Notify（）/Stop时，
		// SIGNABLE/SIGNABLE依次通知此线程相应地更新其信号
		// 掩码。
		sigBlocked := sigset_all
		for i := range sigtable {
			if !blockableSig(uint32(i)) {
				sigdelset(&sigBlocked, i)
			}
		}
		sigprocmask(_SIG_SETMASK, &sigBlocked, nil)
		for {
			select {
			case sig := <-enableSigChan:
				if sig > 0 {
					sigdelset(&sigBlocked, int(sig))
				}
			case sig := <-disableSigChan:
				if sig > 0 && blockableSig(sig) {
					sigaddset(&sigBlocked, int(sig))
				}
			}
			sigprocmask(_SIG_SETMASK, &sigBlocked, nil)
			maskUpdatedChan <- struct{}{}
		}
	}()
}

// 当我们在没有信号堆栈的情况下接收到信号时调用此函数。
// 只有在非Go代码调用sigaltstack以禁用
// 信号堆栈时，才会发生这种情况。
func noSignalStack(sig uint32) {
	println("signal", sig, "received on thread with no signal stack")
	throw("non-Go code disabled sigaltstack")
}

// 如果我们在有信号堆栈的情况下接收到信号，则调用此函数
// 但我们不在其上。只有在名为
// sigaction的非Go代码未设置SS_ONSTACK标志的情况下，才会发生这种情况。
func sigNotOnStack(sig uint32) {
	println("signal", sig, "received but handler not on signal stack")
	throw("non-Go code set up signal handler without SA_ONSTACK flag")
}

// signalDuringFork在执行fork时如果收到信号，则调用。
// 我们当时不需要信号，因为发送到进程的信号
// 组可能会传递到子进程，造成混乱。
// 永远不应该调用此函数，因为我们会阻止信号通过分叉；
// 此函数只是一个安全检查。背景见18600期。
func signalDuringFork(sig uint32) {
	println("signal", sig, "received during fork")
	throw("signal received during fork")
}

var badginsignalMsg = "fatal: bad g in signal handler\n"

// 这在外部堆栈上运行，没有m或g。没有堆栈拆分。
// go:nosplit 
// go:norace 
// go:nowritebarrierrec 
func badsignal(sig uintptr, c *sigctxt) {
	if !iscgo && !cgoHasExtraM {
		// 没有额外的M。needm将无法抓住
		// A M。与其上吊，不如崩溃。
		// 无法调用拆分堆栈函数，因为没有G。
		s := stringStructOf(&badginsignalMsg)
		write(2, s.str, int32(s.len))
		exit(2)
		*(*uintptr)(unsafe.Pointer(uintptr(123))) = 2
	}
	needm()
	if !sigsend(uint32(sig)) {
		// 外部线程接收到信号sig，
		// Go代码不想处理该信号。
		raisebadsignal(uint32(sig), c)
	}
	dropm()
}

// go:noescape 
func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)

// 确定信号是否应由go处理，如果不应由go处理，则将
// 信号转发给go之前安装的处理程序。返回
// 信号是否转发。
// 这是由信号处理程序调用的，世界可能会停止。
// go:nosplit 
// go:nowritebarrierrec 
func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
	if sig >= uint32(len(sigtable)) {
		return false
	}
	fwdFn := atomic.Loaduintptr(&fwdSig[sig])
	flags := sigtable[sig].flags

	// 如果我们没有处理信号，请转发它。
	if atomic.Load(&handlingSig[sig]) == 0 || !signalsOK {
		// 如果忽略信号，则不执行任何操作与转发相同。
		if fwdFn == _SIG_IGN || (fwdFn == _SIG_DFL && flags&_SigIgn != 0) {
			return true
		}
		// 我们没有处理信号，没有其他处理程序可转发。
		// 使用默认行为崩溃。
		if fwdFn == _SIG_DFL {
			setsig(sig, _SIG_DFL)
			dieFromSignal(sig)
			return false
		}

		sigfwd(fwdFn, sig, info, ctx)
		return true
	}

	// 此函数及其调用方sigtrampgo假定SIGPPIPE是在
	// 原始线程上交付的。这个属性不适用于macOS（golang.org/issue/33384），
	// 所以我们别无选择，只能忽略SIGPIPE。
	if (GOOS == "darwin" || GOOS == "ios") && sig == _SIGPIPE {
		return true
	}

	// 如果没有要转发的处理程序，则无需转发。
	if fwdFn == _SIG_DFL {
		return false
	}

	c := &sigctxt{info, ctx}
	// 仅转发同步信号和信号管道。
	// 不幸的是，用户生成的SIGPippes也将被转发，因为si_代码
	// 即使对于从写入闭合套接字
	// 或pipe引发的SIGPippe，也会设置为_si_user。
	if (c.sigcode() == _SI_USER || flags&_SigPanic == 0) && sig != _SIGPIPE {
		return false
	}
	// 确定信号是否发生在Go代码内。我们测试：我们不在VDSO页面，我们在goroutine（即m.curg！=nil），我们不在CGO页面。
	g := sigFetchG(c)
	if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo {
		return false
	}

	// Go未处理信号，请转发。
	if fwdFn != _SIG_IGN {
		sigfwd(fwdFn, sig, info, ctx)
	}

	return true
}

// sigsave将当前线程的信号掩码保存到*p中。
// 当非Go 
// 线程调用Go函数。
// 这是nosplit和nowritebarrierrec，因为它由needm调用
// 它可以在没有可用g的非执行线程上调用。
// go:nosplit 
// go:nowritebarrierrec 
func sigsave(p *sigset) {
	sigprocmask(_SIG_SETMASK, nil, p)
}

// msigrestore将当前线程的信号掩码设置为sigmask。
// 当非Go线程
// 调用Go函数时，用于恢复非Go信号掩码。
// 这是nosplit和nowritebarrierrec，因为它是在g被清除后由dropm 
// 调用的。
// go:nosplit 
// go:nowritebarrierrec 
func msigrestore(sigmask sigset) {
	sigprocmask(_SIG_SETMASK, &sigmask, nil)
}

// sigsetallexit在线程
// 退出时由sigblock使用（true）。sigset_all在特定于操作系统的代码中定义，根据GOOS 
// 行为可能会覆盖sigsetAllExiting的此默认值：请参见
// osinit（）。
var sigsetAllExiting = sigset_all

// sigblock在当前线程的信号掩码中阻塞信号。
// 当一个非Go线程调用Go函数时，该函数用于在设置和拆除g 
// 时阻止信号。当线程退出
// 时，我们使用sigsetAllExiting值，否则使用特定于操作系统的
// sigset_all的定义。
// 这是nosplit和nowritebarrierrec，因为它是由needm调用的
// 可以在没有可用g的非Go线程上调用。
// go:nosplit 
// go:nowritebarrierrec 
func sigblock(exiting bool) {
	if exiting {
		sigprocmask(_SIG_SETMASK, &sigsetAllExiting, nil)
		return
	}
	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
}

// 解除sig从当前线程的信号掩码中删除sig。
// 这是nosplit和nowritebarrierrec，因为它是从
// dieFromSignal调用的，当在信号堆栈上的
// 信号处理程序中运行时，sigfwdgo可以调用它，而没有可用的g。
// go:nosplit 
// go:nowritebarrierrec 
func unblocksig(sig uint32) {
	var set sigset
	sigaddset(&set, int(sig))
	sigprocmask(_SIG_UNBLOCK, &set, nil)
}

// 初始化新m时调用minitSignals以设置
// 线程的备用信号堆栈和信号掩码。
func minitSignals() {
	minitSignalStack()
	minitSignalMask()
}

// 初始化新m时调用minitSignalStack以设置
// 备用信号堆栈。如果未为线程（正常情况）设置备用信号堆栈
// 则将备用信号
// 堆栈设置为gsignal堆栈。如果为线程设置了备用信号堆栈
// 的情况（非Go线程设置备用
// 信号堆栈，然后调用Go函数的情况），则将gsignal 
// 堆栈设置为备用信号堆栈。如果未使用cgo，我们还将备用
// 信号堆栈设置为gsignal堆栈（无论
// 是否已设置）。记录在
// newSigstack中所做的选择，以便在UNMINT中撤消该选择。
func minitSignalStack() {
	_g_ := getg()
	var st stackt
	sigaltstack(nil, &st)
	if st.ss_flags&_SS_DISABLE != 0 || !iscgo {
		signalstack(&_g_.m.gsignal.stack)
		_g_.m.newSigstack = true
	} else {
		setGsignalStack(&st, &_g_.m.goSigStack)
		_g_.m.newSigstack = false
	}
}

// 初始化新m时调用minitSignalMask来设置
// 线程的信号掩码。调用此函数时，线程的所有信号都已被
// 阻塞。这从m.sigmask开始，设置为
// 可以从新创建的线程的initSigmask开始，也可以通过调用
// sigsave（如果这是一个调用Go函数的非Go线程）开始。它将
// 从掩码中删除所有基本信号，从而使这些
// 信号不被阻塞。然后设置线程的信号掩码。
// 调用此函数后，线程可以接收信号。
func minitSignalMask() {
	nmask := getg().m.sigmask
	for i := range sigtable {
		if !blockableSig(uint32(i)) {
			sigdelset(&nmask, i)
		}
	}
	sigprocmask(_SIG_SETMASK, &nmask, nil)
}

// 通过unminit从drop调用unminitSignals，以撤消在非Go线程上调用minit的效果。
// go:nosplit 
func unminitSignals() {
	if getg().m.newSigstack {
		st := stackt{ss_flags: _SS_DISABLE}
		sigaltstack(&st, nil)
	} else {
		// 我们从其他人那里得到了信号堆栈。还原
		// 如果此M被重新用于另一个线程（例如，它是一个额外的M），则恢复Go分配的堆栈。另外，在Android上，libc为所有的线程分配了一个信号栈，因此恢复Go栈非常重要，即使是Go创建的线程，我们也可以释放它。
		restoreGsignalStack(&getg().m.goSigStack)
	}
}

// blockableSig报告sig是否可能被信号掩码阻塞。
// 我们永远不想阻止标记为_SigUnblock的信号；
// 这些同步信号会变成一种恐慌。
// 在Go程序中——不是c-archive/c-shared——我们永远不想阻止
// 标记为_-SigKill或_-SigThrow的信号，否则所有正在运行的线程都可能阻止它们并延迟它们的传递，直到
// 我们启动一个新线程。当链接到C程序时，我们让C代码
// 决定这些信号的处理。
func blockableSig(sig uint32) bool {
	flags := sigtable[sig].flags
	if flags&_SigUnblock != 0 {
		return false
	}
	if isarchive || islibrary {
		return true
	}
	return flags&(_SigKill|_SigThrow) == 0
}

// gsignalStack保存由
// setGsignalStack。
type gsignalStack struct {
	stack       stack
	stackguard0 uintptr
	stackguard1 uintptr
	stktopsp    uintptr
}

// setGsignalStack将当前m的gsignal堆栈设置为
// 从sigaltstack系统调用返回的备用信号堆栈。
// 它将旧值保存在*old中，以供restoreGsignalStack使用。
// 如果非Go代码设置了
// 备用信号堆栈，则在处理信号时使用此选项。
// go:nosplit 
// go:nowritebarrierrec 
func setGsignalStack(st *stackt, old *gsignalStack) {
	g := getg()
	if old != nil {
		old.stack = g.m.gsignal.stack
		old.stackguard0 = g.m.gsignal.stackguard0
		old.stackguard1 = g.m.gsignal.stackguard1
		old.stktopsp = g.m.gsignal.stktopsp
	}
	stsp := uintptr(unsafe.Pointer(st.ss_sp))
	g.m.gsignal.stack.lo = stsp
	g.m.gsignal.stack.hi = stsp + st.ss_size
	g.m.gsignal.stackguard0 = stsp + _StackGuard
	g.m.gsignal.stackguard1 = stsp + _StackGuard
}

// restoreGsignalStack在进入信号处理程序之前，将gsignal堆栈还原为其拥有的值
// 。
// go:nosplit 
// go:nowritebarrierrec 
func restoreGsignalStack(st *gsignalStack) {
	gp := getg().m.gsignal
	gp.stack = st.stack
	gp.stackguard0 = st.stackguard0
	gp.stackguard1 = st.stackguard1
	gp.stktopsp = st.stktopsp
}

// /signalstack将当前线程的备用信号堆栈设置为s。
// go:nosplit 
func signalstack(s *stack) {
	st := stackt{ss_size: s.hi - s.lo}
	setSignalstackSP(&st, s.lo)
	sigaltstack(&st, nil)
}

// /setsigsegv在darwin/arm64上用于伪造分段错误。
// 
// 这是在运行时/cgo中通过linkname导出到程序集的。
// 
// go:nosplit 
// go:linkname设置IGSEGV 
func setsigsegv(pc uintptr) {
	g := getg()
	g.sig = _SIGSEGV
	g.sigpc = pc
	g.sigcode0 = _SEGV_MAPERR
	g.sigcode1 = 0 // TODO:模拟si_地址
}
